package com.yy.tech.data.coder.secret.utils;

import com.yy.tech.data.context.SecretContent;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.util.Base64;

/**
 * @author yy
 * @date 2022/4/4
 * @description: TODO
 */
public final class AES256Encryptor {
    public static final String PREFIX = "{AES}";
    public static final String ALGORITHM = "AES";

    private AES256Encryptor() {
    }

    private static Cipher createEncryptCipher(final String key, final String iv) throws Exception {
        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
        final IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
        cipher.init(1, keySpec, ivParameterSpec);
        return cipher;
    }

    private static Cipher createDecryptCipher(final String key, final String iv) throws Exception {
        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
        final IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
        cipher.init(2, keySpec, ivParameterSpec);
        return cipher;
    }

    private static void keyIVCheck(final String key, final String iv) throws Exception {
        if (key == null) {
            throw new Exception("key can't be null!");
        } else if (key.length() != 32) {
            throw new Exception("key must be 32 bytes!");
        } else if (iv == null) {
            throw new Exception("iv can't be null!");
        } else if (iv.length() != 16) {
            throw new Exception("iv must be 16 bytes!");
        }
    }

    public static byte[] encrypt(final byte[] bytes, final String key, final String iv) {
        try {
            final Cipher cipher = createEncryptCipher(key, iv);
            final byte[] encryptData = cipher.doFinal(bytes);
            return encryptData;
        } catch (final Throwable var5) {
            throw new RuntimeException(var5);
        }
    }

    public static byte[] decrypt(final byte[] bytes, final String key, final String iv) {
        try {
            final Cipher cipher = createDecryptCipher(key, iv);
            final byte[] original = cipher.doFinal(bytes);
            return original;
        } catch (final Exception var5) {
            throw new RuntimeException(var5);
        }
    }

    public static String encryptAsBase64(final String srcData, final String key, final String iv,
                                         final boolean checkAsciiPrintable) throws Exception {
        if (org.springframework.util.StringUtils.isEmpty(srcData)) {
            return srcData;
        } else if (srcData.startsWith(PREFIX)) {
            throw new RuntimeException("Data is already encrypted,current:" + srcData);
        } else {
            keyIVCheck(key, iv);
            if (checkAsciiPrintable) {
                throw new RuntimeException("Data to be encrypted is not ascii printable,current:" + srcData);
            } else {
                byte[] bytes;
                try {
                    bytes = srcData.getBytes(SecretContent.DEFAULT_CHARSET);
                } catch (final UnsupportedEncodingException var6) {
                    throw new RuntimeException(var6);
                }

                final byte[] encryptData = encrypt(bytes, key, iv);
                return PREFIX + Base64.getEncoder().encodeToString(encryptData);
            }
        }
    }

    public static String decryptFromBase64(String encryptedData, final String key, final String iv,
                                           final boolean checkAsciiPrintable) throws Exception {
        if (org.springframework.util.StringUtils.isEmpty(encryptedData)) {
            return encryptedData;
        } else if (!encryptedData.startsWith(PREFIX)) {
            throw new RuntimeException("Decrypted data must be start with {AES},current:" + encryptedData);
        } else {
            keyIVCheck(key, iv);
            encryptedData = encryptedData.substring(PREFIX.length());
            final byte[] bytes = Base64.getDecoder().decode(encryptedData);
            final byte[] original = decrypt(bytes, key, iv);
            String result;
            try {
                result = new String(original, SecretContent.DEFAULT_CHARSET);
            } catch (final UnsupportedEncodingException var8) {
                throw new RuntimeException(var8);
            }

            if (checkAsciiPrintable) {
                throw new RuntimeException(
                        "Decrypted data is not ascii printable, maybe using invalid key/iv/mode when decrypt data. Current:"
                                + encryptedData);
            } else {
                return result;
            }
        }
    }
}
